﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Net.Sockets;
using System.Net;
using Rung.Net.Base;
using System.ComponentModel;
using Rung.Net.Enums;
using System.IO;
using Rung.Net.Events;

namespace Rung.Net.Cache
{
    public abstract class CacheStream : NetBaseStream<byte[]>
    {
        /// <summary>
        /// 在 Send(byte[] data) 方法发起的传输中，表示完整数据。
        /// 消息发起方会将该数据分包发送，消息接收方会将接收到的分包数据组合到该数据中。
        /// </summary>
        protected byte[] buffer;
        /// <summary>
        /// 该 CacheStream 所在的端是否为发送者端。
        /// </summary>
        bool? isSender;
        protected string oriFileName;
        /// <summary>
        /// 保存接收文件的临时文件。
        /// 该文件的实体在接收方收到“一次握手”通知时创建。
        /// </summary>
        protected FileInfo tempFile;
        /// <summary>
        /// 当前 已发送/已接受 的包的总数。
        /// </summary>
        protected int packageCountReceived
        { get; set; }
        /// <summary>
        /// 待发送文件的总长度。
        /// </summary>
        protected long fileLength = 0;
        /// <summary>
        /// 指示该对象是否正在工作中（是否存在未传完的文件）。
        /// </summary>
        public bool IsBusy { get; set; }
        /// <summary>
        /// 在发起新的文件发送请求前，是否检测当前的网络流中存在未传送完毕的文件。
        /// 同一网络流中，不能同时传送两个文件，因为服务端无法识别接收到的数据包的归属。
        /// </summary>
        public bool IgnoreBusyCheck { get; set; }

        /// <summary>
        ///  在该模式下，默认每个包容纳 10k 的字节。
        /// </summary>
        public CacheStream(NetworkStream stream, EndPoint endpoint)
            : this(stream, endpoint, 10240) { }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="packageMaxLength">每个包所能容纳的数据量</param>
        public CacheStream(NetworkStream stream, EndPoint endpoint, int packageMaxLength)
            : base(stream, endpoint, packageMaxLength) { }

        public override void Send(byte[] data)
        {
            // 标记为繁忙
            this.IsBusy = true;

            this.tempFile = NetUtility.GetNewTempFile(data.Length);
            CacheStream.WriteFile(tempFile, data);
            this.Send(this.tempFile.FullName, false);
        }

        public void Send(string fullFileName)
        {
            this.Send(fullFileName, true);
        }

        public void Send(string fullFileName, bool needCheck)
        {
            if (needCheck && !IgnoreBusyCheck && this.IsBusy) throw (new Exception("网络流传送其他文件仍未结束。"));

            // 标记为繁忙
            this.IsBusy = true;

            tempFile = new FileInfo(fullFileName);
            if (!tempFile.Exists) throw (new FileNotFoundException());

            this.isSender = true;
            // 该文件的原文件名（也就是发送方文件系统中的文件名）
            this.oriFileName = tempFile.Name;

            //if (tempFile.Length <= base.NetBasePackageMaxLength - 4)
            //{
            //    #region 单次传送即可完成，则直接搞定
            //    byte[] data = CacheStream.ReadFile(tempFile);
            //    byte[] bsMessage = new byte[data.Length + 4];
            //    System.Buffer.BlockCopy(BitConverter.GetBytes(MessageTypeBase.Cache_SiglePackage), 0, bsMessage, 0, 4);
            //    System.Buffer.BlockCopy(data, 0, bsMessage, 4, data.Length);
            //    base.SendRaw(bsMessage);
            //    #endregion
            //}
            //else
            {
                #region 构造发送“一次握手”包
                // 包容量&文件总长度&原文件名
                string body = string.Format("{1}{0}{2}{0}{3}",
                                            LevelSeperators.Level1,
                                            base.NetBasePackageMaxLength.ToString(),
                                            tempFile.Length,
                                            this.oriFileName);

                //message = NetUtility.GetFullMessage(MessageTypeBase.Cache_PackageMaxLength1, body);
                byte[] b = NetUtility.C(body);
                byte[] bBody = new byte[b.Length + IntLength];
                System.Buffer.BlockCopy(BitConverter.GetBytes(MessageTypeBase.Cache_PackageMaxLength1), 0, bBody, 0, IntLength);
                System.Buffer.BlockCopy(b, 0, bBody, IntLength, b.Length);
                base.SendRaw(bBody);
                #endregion
            }
        }

        protected override void ReceivedRaw(byte[] bytes)
        {
            //if (bytes.Length < 4) return;

            //int type = BitConverter.ToInt32(bytes, 0);
            //byte[] bBody = new byte[bytes.Length - IntLength];
            //System.Buffer.BlockCopy(bytes, IntLength, bBody, 0, bBody.Length);

            //switch (type)
            //{
            //    case MessageTypeBase.Cache_SiglePackage:// 单次传送即可完成。
            //        {
            //            this.tempFile = NetUtility.GetNewTempFile(bBody.Length);
            //            WriteFile(this.tempFile, bBody);

            //            this.DoRaiseOnReceived();
            //            break;
            //        }
            //    default:
            //        break;
            //}
        }

        #region File
        private static byte[] ReadFile(FileInfo fi)
        {
            if (fi.Length > int.MaxValue) throw (new OverflowException("文件太大，一次无法完全读取。"));

            return ReadFile(fi, 0, (int)fi.Length);
        }

        private static byte[] ReadFile(FileInfo fi, int start, int length)
        {
            byte[] result = new byte[length];
            using (FileStream fs = fi.OpenRead())
            {
                fs.Read(result, start, length);
                return result;
            }
        }

        private static void WriteFile(FileInfo tempFile, byte[] bBody)
        {
            WriteFile(tempFile, bBody, 0);
        }

        private static void WriteFile(FileInfo tempFile, byte[] bBody, int index)
        {
            using (FileStream fs = tempFile.OpenWrite())
            {
                fs.Write(bBody, index, bBody.Length);
            }
        }

        protected void DoRaiseOnReceived()
        {
            // 文件长&源文件名（短名称：a.txt）&目标文件名（长名称：c:\8e502e061f18412b84bb51c7bf36848e.dat）
            string fileLength = tempFile.Length.ToString();
            string oriFileName = this.oriFileName;
            string desFileName = tempFile.FullName;

            string body = NetUtility.GetFullMessage(MessageTypeBase.CacheFinished,
                string.Format("{1}{0}{2}{0}{3}", LevelSeperators.Level1, fileLength, oriFileName, desFileName));
            RaiseOnReceived(NetUtility.C(body));
        }
        #endregion
    }
}
